#!/usr/bin/python3
# by Sun Smallwhite <niasw@pku.edu.cn>(https://github.com/niasw)
# this branch only measure dyp algorithm, it collects the best parallel ratio when clusnum = 2 to setting value

import sw.calc.calcMat
import sw.calc.dypClus
import sw.io.loadCSV
import sw.io.saveJSON
import sw.io.adaptor
import scipy.sparse
import numpy
import json
import time

timestart=0.;
timefinal=0.;

def dypMethod(adjmat,clusnum=40,monitor=False):
  stats=[];
  timestart=time.clock(); # >>>
  eigvs=sw.calc.calcMat.maxEigVec(adjmat);
  timefinal=time.clock(); # <<<
  print('Time for maxEigVec: '+str(timefinal-timestart)+' s');
  eigvec=eigvs['eigenvector'];
  eigval=eigvs['eigenvalue'];
  timestart=time.clock(); # >>>
  cluslist=sw.calc.dypClus.calcClusters(eigvec,clusnum=clusnum,monitor=monitor,outputall=True);
  timefinal=time.clock(); # <<<
  print('Time for calcClusters (dyp_all): '+str(timefinal-timestart)+' s');
  stats.append({'time':(timefinal-timestart)});
  timestart=time.clock(); # >>>
  for cnum in range(2,len(cluslist)+2):
   clusters=cluslist[cnum-2];
   xvec=sw.calc.calcMat.calcXvector(clusters,len(eigvec));
   dotprod=xvec.dot(eigvec);
   unparallel=numpy.sqrt(1-dotprod**2/numpy.float64(cnum)/eigvec.dot(eigvec)); # sqrt(1-(x.a)(x.a)/(x.x)/(a.a)),the relative length of perpendicular part
   primarycomponent=dotprod**2*eigval/eigvec.dot(eigvec); # eigval*(x.a)^2 is the max component of the total flow. we only maximize this.
   continuouslimit=numpy.float64(cnum)*eigval; # x//a is the best situation which only continuous problem can reach. our discrete problem should be lower than that.
   totalflow=xvec.dot(xvec*adjmat); # total flow = x'Ax
   stats[0][str(cnum)]={'unparallel':numpy.float64(unparallel.real),'primarycomponent':numpy.float64(primarycomponent.real),'continuouslimit':numpy.float64(continuouslimit.real),'dotprod':numpy.float64(dotprod.real),'clusnum':len(clusters),'nodenum':len(eigvec),'totalflow':numpy.float64(totalflow.real)};
   cluslist[cnum-2]=sw.calc.calcMat.simplify(None,clusters);
   timefinal=time.clock(); # <<<
   print('Time for quantity calculation and organizing result: '+str(timefinal-timestart)+' s');
  return stats,cluslist;

def collectBest(in_linkfile,out_clusprefix,clusnum=40,indexstart=0,monitor=False,localoutput=False):
  '''
# collects the best parallel ratio when clusnum = 2 to setting value
#
# input:
#  in_linkfile: the file stores links. (sparse matrix coo format)
#  out_clusprefix: output filename prefix
#  clusnum: the cluster number requests
#  indexstart: index start from 1
#  monitor: see eigenvector figures and error reports
#  localoutput: create data file for Sun Sibai's preview engine
# output:
#  write clustering result into files: out_clusprefix.clustering_times.paper_number.cluster_number
  '''
  stats=[]; # statistical items
  print('Loading Link Data & Number Vector Data ...');
  timestart=time.clock(); # >>>
  adjmat=sw.io.loadCSV.linkCSV2adjMat('../dat/'+in_linkfile,indexstart=indexstart,withdata=False);
  timefinal=time.clock(); # <<<
  print('Time for loading adjacent matrix: '+str(timefinal-timestart)+' s');
  print('Total Node(Paper) Number: '+str(adjmat.shape[0]));
  if (adjmat.shape[0]!=adjmat.shape[1]):
    raise(Exception('Adjacent Matrix is not square. Row:'+str(adjmat.shape[0])+' ,Col:'+str(adjmat.shape[1])));
  print('Stair Clustering ['+str(clusnum)+'] ...');
  stats,cluslist=dypMethod(adjmat,clusnum=clusnum,monitor=monitor);
  flowmat=adjmat;
  for cnum in range(2,len(cluslist)+2):
   clusters=cluslist[cnum-2];
   if (cnum<adjmat.shape[0]): # otherwise the clusters do not change.
     #timestart=time.clock(); # >>>
     #flowmat=sw.calc.calcMat.flowMat(adjmat,clusters);
     #timefinal=time.clock(); # <<<
     #print('Time for flow matrix calculation (cnum='+str(cnum)+'): '+str(timefinal-timestart)+' s');
     #stats[0][str(cnum)]['timeflowmatcalc']=timefinal-timestart;
     #if monitor:
       #print('-> Number of Clusters = '+str(len(cluslist)));
       #print('-> Total Flow = '+str(flowmat.sum()));
       #print('-> Total Flow Square = '+str(numpy.linalg.norm(flowmat,ord='fro')**2));
     if (localoutput):
       timestart=time.clock(); # >>>
       sw.io.saveJSON.saveJSON('../out/'+out_clusprefix+'_clus.'+str(cnum)+'.json',clusters);
       #sw.io.saveJSON.saveJSON('../out/'+out_clusprefix+'_flow.'+str(cnum)+'.json',flowmat.tolist());
       timefinal=time.clock(); # <<<
       print('Time for saving local results (flow and clus): '+str(timefinal-timestart)+' s');
   #stats[0][str(cnum)]['flow']=flowmat.sum();
  timestart=time.clock(); # >>>
  sw.io.saveJSON.saveJSON('../out/'+out_clusprefix+'_stat.'+str(clusnum)+'.json',stats);
  timefinal=time.clock(); # <<<
  print('Time for quality criteria saving (parallel & total flow): '+str(timefinal-timestart)+' s');

if (__name__=='__main__'):
  '''
# python collectBest.py in_linkfile out_clusprefix clusnum indexstart=0 monitor=False localoutput=False
# this branch only measure dyp algorithm, it collects the best parallel ratio when clusnum = 2 to setting value
  '''
  import sys
  args=[None,'','',40,0,'False','False'];
  for it in range(0,len(sys.argv)):
    args[it]=sys.argv[it];
  collectBest(args[1],args[2],int(args[3]),int(args[4]),args[5]=='True',args[6]=='True');
